package com.mallcloud.mall.app.config;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * @Author:
 * @Description:
 * @from https://blog.csdn.net/qq_45950109/article/details/126890958
 **/
@Slf4j
@Data
@Component
@Configuration
@ConditionalOnProperty("const.wxpay.v3.appId")
@ConfigurationProperties(prefix = "const.wxpay.v3")
public class WechatPayV3Config {

    /**
     * 应用编号
     */
    private String appId;
    private String appSecret;
    /**
     * 商户号
     */
    private String mchId;
    /**
     * 服务商商户号
     */
    private String slMchId;
    /**
     * APIv3密钥
     */
    private String apiKey;

    /**
     * API 证书中的 key.pem
     */
    private String keyPemPath;

    /**
     * 商户序列号
     */
    private String serialNo;

    /**
     * 微信支付V3-url前缀
     */
    private String baseUrl;

    /**
     * 微信支付v3 支付通知回调地址
     */
    private String notifyUrl;

    /**
     * 微信支付v3 退款回调地址
     */
    private String refundNotifyUrl;

    /**
     * 获取证书管理器实例
     *
     * @return
     */
    @Bean
    public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {
        log.info("获取证书管理器实例");
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(keyPemPath);

        //TODO 打印私钥
        //printPrivateKey(this.getClass().getClassLoader().getResourceAsStream(keyPemPath));

        //私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey);
        log.info("证书序列号: {}", serialNo);

        //身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
        log.info("商户号: {}", mchId);

        // 使用定时更新的签名验证器，不需要传入证书
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiKey.getBytes(StandardCharsets.UTF_8));

        return certificatesManager.getVerifier(mchId);
    }

    /**
     * sign
     *
     * @return
     */
    @Bean(name = "wxPay3Sign")
    public Sign getSign() {
        log.info("获取v3 sign实例");
        PrivateKey privateKey = getPrivateKey(keyPemPath);
        return SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKey.getEncoded(), null);
    }


    /**
     * 获取支付http请求对象
     *
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(Verifier verifier) {
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(keyPemPath);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, serialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        return builder.build();
    }

    /**
     * 获取HttpClient，无需进行应答签名验证，跳过验签的流程
     */
    @Bean(name = "wxPayNoSignClient")
    public CloseableHttpClient getWxPayNoSignClient() {
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(keyPemPath);

        //用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                //设置商户信息
                .withMerchant(mchId, serialNo, privateKey)
                //无需进行签名验证、通过withValidator((response) -> true)实现
                .withValidator((response) -> true);

        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        return builder.build();
    }

    /**
     * 获取商户的私钥文件
     *
     * @param keyPemPath
     * @return
     */
    private PrivateKey getPrivateKey(String keyPemPath) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath);
        if (inputStream == null) {
            throw new RuntimeException("私钥文件不存在");
        }
        return PemUtil.loadPrivateKey(inputStream);
    }

    private void printPrivateKey(InputStream inputStream) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(2048);
        byte[] buffer = new byte[1024];

        String privateKey;
        try {
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                os.write(buffer, 0, length);
            }

            privateKey = os.toString("UTF-8");
        }
        catch (IOException var5) {
            throw new IllegalArgumentException("无效的密钥", var5);
        }
        log.info("私钥");
        System.out.println(privateKey);
    }
}
